package com.sprucetec.live.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.sprucetec.live.common.exceptioin.SprucetecException;
import com.sprucetec.live.enums.LoadOSSFileType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: zhangyb
 * Date: 2018/11/27
 * Time: 15:11
 * Description:
 **/
@Slf4j
public class OssUtil {

    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
    private static final int DEFAULT_SOCKET_TIMEOUT = 5000;
    private static final int DEFAULT_CONNECT_TIMEOUT = 5000;
    private static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 5000;

    public static Map createOssHeader(String tag, String key, long expires, String uid, String uname, String object, JSONArray options) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update((tag + key + expires).getBytes("utf-8"));
        byte[] bytes = md5.digest();
        StringBuilder ret = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
        }
        String token = ret.toString().toLowerCase();

        Encoder urlBase64Encoder = Base64.getUrlEncoder();

        Map<String, Object> http_oss_auth_map = new HashMap<>();
        http_oss_auth_map.put("tag", tag);
        http_oss_auth_map.put("expires", expires);
        http_oss_auth_map.put("token", token);
        String HTTP_OSS_AUTH = urlBase64Encoder.encodeToString(JSON.toJSONString(http_oss_auth_map).getBytes("UTF-8"));
        Map<String, Object> http_oss_param_map = new HashMap<>();
        try {
            http_oss_param_map.put("uid", Long.valueOf(uid.trim()));
        } catch (NumberFormatException e) {
            http_oss_param_map.put("uid", "99999999");
        }
        http_oss_param_map.put("uname", uname);
        http_oss_param_map.put("object", object);
        http_oss_param_map.put("options", options);
        String HTTP_OSS_PARAM = urlBase64Encoder.encodeToString(JSON.toJSONString(http_oss_param_map).getBytes("UTF-8"));
        Map<String, String> extraHeader = new HashMap<String, String>();
        extraHeader.put("Oss-Auth", HTTP_OSS_AUTH);
        extraHeader.put("Oss-Param", HTTP_OSS_PARAM);
        extraHeader.put("App-Code", "mams-admin");
        return extraHeader;
    }

    /**
     * 上传附件 (MultipartFile)
     *
     * @param url
     * @param uploadFile
     * @param extraHeader
     * @return
     * @throws Exception
     */
    public static String uploadFile(String url, MultipartFile uploadFile, Map<String, String> extraHeader) throws Exception {
        SSLHelper sslHelper = new SSLHelper();
        PoolingHttpClientConnectionManager pcm = sslHelper.GetPcm();
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(pcm).setDefaultRequestConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build()).build();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectTimeout(OssUtil.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build());
        httpPost.addHeader("Content-Type", "application/octet-stream");
        httpPost.addHeader("Connection", "Keep-Alive");
        httpPost.addHeader("user-agent", "MEICAI/WMC");
        for (String key : extraHeader.keySet()) {
            httpPost.addHeader(key, extraHeader.get(key));
        }
        CloseableHttpResponse response = null;
        String responseString = null;
        InputStream is = null;
        try {
            if (uploadFile != null) {
                is = uploadFile.getInputStream();
                InputStreamEntity reqEntity = new InputStreamEntity(is, -1);
                reqEntity.setContentType("binary/octet-stream");
                reqEntity.setChunked(true);
                httpPost.setEntity(reqEntity);
            }
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (is != null) {
                is.close();
            }
            if (entity != null) {
                return EntityUtils.toString(entity);
            }
        } catch (IOException e) {
            log.error("uploadFile in OssUtil error", e);
            throw new SprucetecException("上传附件失败");
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                log.error("uploadFile in OssUtil error", e);
            }
        }
        return responseString;
    }


    /**
     * 上传File 到OSS
     *
     * @param url
     * @param uploadFile
     * @param extraHeader
     * @return
     * @throws Exception
     */
    public static String uploadFileGen(String url, File uploadFile, Map<String, String> extraHeader) throws Exception {
        SSLHelper sslHelper = new SSLHelper();
        PoolingHttpClientConnectionManager pcm = sslHelper.GetPcm();
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(pcm).setDefaultRequestConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build()).build();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectTimeout(OssUtil.DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build());
        httpPost.addHeader("Content-Type", "application/octet-stream");
        httpPost.addHeader("Connection", "Keep-Alive");
        httpPost.addHeader("user-agent", "MEICAI/WMC");
        for (String key : extraHeader.keySet()) {
            httpPost.addHeader(key, extraHeader.get(key));
        }
        CloseableHttpResponse response = null;
        String responseString = null;
        InputStream is = null;
        try {
            if (uploadFile != null) {
                is = new FileInputStream(uploadFile);
                InputStreamEntity reqEntity = new InputStreamEntity(is, -1);
                reqEntity.setContentType("binary/octet-stream");
                reqEntity.setChunked(true);
                httpPost.setEntity(reqEntity);
            }
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (is != null) {
                is.close();
            }
            if (entity != null) {
                return EntityUtils.toString(entity);
            }
        } catch (IOException e) {
            log.error("uploadFile in OssUtil error", e);
            throw new SprucetecException("上传附件失败");
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                log.error("uploadFile in OssUtil error", e);
            }
        }
        return responseString;
    }

    /**
     * 下载oss附件
     *
     * @param fileName            带文件后缀的文件名
     * @param fileUrl             附件完整ossUrl
     * @param httpServletResponse
     */
    public static void downloadAttachment(String fileName, String fileUrl, LoadOSSFileType loadOSSFileType, HttpServletResponse httpServletResponse) {
        log.info("start downloadAttachment fileName = {}, fileUrl = {}", fileName, fileUrl);
        OutputStream out = null;
        InputStream in = null;
        CloseableHttpClient httpClient = null;
        try {
            if (loadOSSFileType == LoadOSSFileType.PREVIEW) {
                setPreviewContentType(httpServletResponse, fileName);
            } else {
                setDownloadContentType(httpServletResponse, fileName);
            }
            out = httpServletResponse.getOutputStream();
            HttpGet httpGet = new HttpGet(fileUrl);
            SSLHelper sslHelper = new SSLHelper();
            PoolingHttpClientConnectionManager pcm = sslHelper.GetPcm();
            httpClient = HttpClients.custom().setConnectionManager(pcm).setDefaultRequestConfig(RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIMEOUT).setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT).build()).build();
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity entity = httpResponse.getEntity();
            in = entity.getContent();
            byte[] buffer = new byte[4096];
            int readLength = 0;
            while ((readLength = in.read(buffer)) > 0) {
                byte[] bytes = new byte[readLength];
                System.arraycopy(buffer, 0, bytes, 0, readLength);
                out.write(bytes);
            }
        } catch (IOException e) {
            log.error("downloadAttachment in OssUtil error", e);
        } finally {
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    log.error("httpClient.close in OssUtil at downloadAttachment error", e);
                }
            }
            if (out != null) {
                try {
                    out.flush();
                } catch (IOException e) {
                    log.error("out.flush error in OssUtil at downloadAttachment OssUtil", e);
                }
                try {
                    out.close();
                } catch (IOException e) {
                    log.error("out.close error in OssUtil at downloadAttachment OssUtil", e);
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("in.close error in OssUtil at downloadAttachment OssUtil", e);
                }
            }
        }
    }

    public static void setDownloadContentType(HttpServletResponse httpServletResponse, String fileName) throws UnsupportedEncodingException {
        httpServletResponse.setContentType("multipart/form-data");
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + new String(fileName.getBytes("GBK"), "ISO8859_1"));
    }

    public static void setPreviewContentType(HttpServletResponse httpServletResponse, String fileName) throws UnsupportedEncodingException {
        httpServletResponse.setHeader("Content-disposition", "inline;filename=" + new String(fileName.getBytes("GBK"), "ISO8859_1"));
    }


    /**
     * 获取用户客户端IP
     *
     * @param request
     * @return
     */
    public static String getRealIp(HttpServletRequest request) {
        if (request == null) {
            log.error("error", new Exception("getRealIp method HttpServletRequest Object is null"));
            return null;
        }
        String ipString = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("remote_addr");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("X-Forwarded-For");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
            ipString = request.getRemoteAddr();
        }

        // 多个路由时，取第一个非unknown的ip
        final String[] arr = ipString.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ipString = str;
                break;
            }
        }
        return ipString;
    }
}
